package com.atguigu.tingshu.album.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.Assert;
import com.atguigu.tingshu.album.mapper.AlbumInfoMapper;
import com.atguigu.tingshu.album.mapper.AlbumStatMapper;
import com.atguigu.tingshu.album.mapper.TrackInfoMapper;
import com.atguigu.tingshu.album.mapper.TrackStatMapper;
import com.atguigu.tingshu.album.service.TrackInfoService;
import com.atguigu.tingshu.album.service.VodService;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.model.album.AlbumInfo;
import com.atguigu.tingshu.model.album.AlbumStat;
import com.atguigu.tingshu.model.album.TrackInfo;
import com.atguigu.tingshu.model.album.TrackStat;
import com.atguigu.tingshu.query.album.TrackInfoQuery;
import com.atguigu.tingshu.user.client.UserFeignClient;
import com.atguigu.tingshu.vo.album.*;
import com.atguigu.tingshu.vo.user.UserInfoVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Service
@SuppressWarnings({"all"})
public class TrackInfoServiceImpl extends ServiceImpl<TrackInfoMapper, TrackInfo> implements TrackInfoService {

    @Autowired
    private TrackInfoMapper trackInfoMapper;


    @Autowired
    private AlbumInfoMapper albumInfoMapper;

    @Autowired
    private VodService vodService;

    /**
     * 接口下包含多个实现类对象 解决：
     * 方式一：配合@Qualifier("指定注入Bean名称")
     * 方式二：@Resource(name = "指定注入Bean名称")
     */
    @Autowired //按类型注入
    private TrackStatMapper trackStatMapper;

    @Autowired
    private UserFeignClient userFeignClient;


    @Autowired
    private AlbumStatMapper albumStatMapper;

    /**
     * 保存声音，关联到专辑
     *
     * @param trackInfoVo 声音VO对象
     * @param userId      用户ID
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveTrackInfo(TrackInfoVo trackInfoVo, Long userId) {
        //1.根据专辑ID查询专辑，获取包含声音数量，更新专辑包含声音数量
        AlbumInfo albumInfo = albumInfoMapper.selectById(trackInfoVo.getAlbumId());
        Assert.notNull(albumInfo, "专辑:{}不存在", trackInfoVo.getAlbumId());
        Integer includeTrackCount = albumInfo.getIncludeTrackCount();

        //2.将提交声音VO转为PO对象，给PO对象进行属性赋值（包括调用点播平台获取音频文件时长，大小，类型）
        TrackInfo trackInfo = BeanUtil.copyProperties(trackInfoVo, TrackInfo.class);
        //2.1.1 属性用户ID赋值
        trackInfo.setUserId(userId);
        //2.1.2 声音序号 增量+1
        trackInfo.setOrderNum(includeTrackCount + 1);
        //2.1.3 封面图片，如果有提交则用提交的，反之用专辑封面图片
        if (StringUtils.isBlank(trackInfo.getCoverUrl())) {
            trackInfo.setCoverUrl(albumInfo.getCoverUrl());
        }
        //2.1.4 封装音频文件时长，大小，类型 从点播平台获取
        TrackMediaInfoVo trackMediaInfoVo = vodService.getTrackMediaInfo(trackInfo.getMediaFileId());
        if (trackMediaInfoVo != null) {
            trackInfo.setMediaType(trackMediaInfoVo.getType());
            trackInfo.setMediaDuration(BigDecimal.valueOf(trackMediaInfoVo.getDuration()));
            trackInfo.setMediaSize(trackMediaInfoVo.getSize());
        }
        //2.1.5 封装来源：用户原创
        trackInfo.setSource(SystemConstant.TRACK_SOURCE_USER);
        //2.1.6 封装状态：未审核
        trackInfo.setStatus(SystemConstant.TRACK_STATUS_NO_PASS);
        //2.2 保存声音
        trackInfoMapper.insert(trackInfo);

        //3.TODO 对音视频文件开启内容审核，将审核任务ID关联到声音表中，为后续查询审核结果做准备
        String reviewTaskId = vodService.startReviewTask(trackInfo.getMediaFileId());
        if (StringUtils.isNotBlank(reviewTaskId)) {
            trackInfo.setStatus(SystemConstant.TRACK_STATUS_REVIEWING);
            trackInfo.setReviewTaskId(reviewTaskId);
            trackInfoMapper.updateById(trackInfo);
        }

        //4.更新专辑包含声音数量
        albumInfo.setIncludeTrackCount(includeTrackCount + 1);
        albumInfoMapper.updateById(albumInfo);

        //5.初始化保存声音统计数值
        this.saveTrackStat(trackInfo.getId(), SystemConstant.TRACK_STAT_PLAY, 0);
        this.saveTrackStat(trackInfo.getId(), SystemConstant.TRACK_STAT_COLLECT, 0);
        this.saveTrackStat(trackInfo.getId(), SystemConstant.TRACK_STAT_PRAISE, 0);
        this.saveTrackStat(trackInfo.getId(), SystemConstant.TRACK_STAT_COMMENT, 0);
    }

    /**
     * 初始化声音统计信息
     *
     * @param id       声音ID
     * @param statType
     * @param num
     */
    @Override
    public void saveTrackStat(Long id, String statType, int num) {
        TrackStat trackStat = new TrackStat();
        trackStat.setTrackId(id);
        trackStat.setStatType(statType);
        trackStat.setStatNum(num);
        trackStatMapper.insert(trackStat);
    }

    /**
     * 分页查询当前用户声音列表
     *
     * @param pageInfo       分页对象
     * @param trackInfoQuery 查询条件
     * @return
     */
    @Override
    public Page<TrackListVo> getUserTrackPage(Page<TrackListVo> pageInfo, TrackInfoQuery trackInfoQuery) {
        return trackInfoMapper.getUserTrackPage(pageInfo, trackInfoQuery);
    }

    /**
     * 修改声音信息
     *
     * @param id          声音ID
     * @param trackInfoVo 声音VO信息
     * @return
     */
    @Override
    public void updateTrackInfo(Long id, TrackInfoVo trackInfoVo) {
        //1. 根据声音ID查询声音，获取修改前声音文件标识
        TrackInfo trackInfo = trackInfoMapper.selectById(id);
        String mediaFileIdOld = trackInfo.getMediaFileId();
        //2. 判断声音文件是否修改，如果修改了，获取新文件媒体信息，开启内容审核；从点播平台删除旧文件
        String mediaFileIdNew = trackInfoVo.getMediaFileId();


        BeanUtil.copyProperties(trackInfoVo, trackInfo);
        if (!mediaFileIdNew.equals(mediaFileIdOld)) {
            //2.1 获取变更后音频文件详情
            TrackMediaInfoVo trackMediaInfo = vodService.getTrackMediaInfo(mediaFileIdNew);
            if (trackMediaInfo != null) {
                trackInfo.setMediaType(trackMediaInfo.getType());
                trackInfo.setMediaDuration(BigDecimal.valueOf(trackMediaInfo.getDuration()));
                trackInfo.setMediaSize(trackMediaInfo.getSize());
            }
            //2.2 TODO 对新音频文件发起审核任务
            String reviewTaskId = vodService.startReviewTask(mediaFileIdNew);
            if (StringUtils.isNotBlank(reviewTaskId)) {
                trackInfo.setStatus(SystemConstant.TRACK_STATUS_REVIEWING);
                trackInfo.setReviewTaskId(reviewTaskId);
                trackInfoMapper.updateById(trackInfo);
            }
        }
        //3. 更新声音信息
        trackInfoMapper.updateById(trackInfo);
    }

    /**
     * 删除声音
     *
     * @param id
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void removeTrackInfo(Long id) {
        //1. 根据ID查询被删除声音信息，获取专辑ID跟序号确保顺序连续
        TrackInfo trackInfo = trackInfoMapper.selectById(id);
        Integer orderNum = trackInfo.getOrderNum();
        Long albumId = trackInfo.getAlbumId();

        //2. 更新当前专辑下声音序号，确保连续
        LambdaUpdateWrapper<TrackInfo> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(TrackInfo::getAlbumId, albumId);
        updateWrapper.gt(TrackInfo::getOrderNum, orderNum);
        updateWrapper.setSql("order_num = order_num - 1");
        trackInfoMapper.update(null, updateWrapper);

        //3. 删除声音信息
        trackInfoMapper.deleteById(id);

        //4. 删除声音统计信息
        trackStatMapper.delete(new LambdaQueryWrapper<TrackStat>().eq(TrackStat::getTrackId, id));

        //5. 删除点播平台声音文件
        vodService.deleteMedia(trackInfo.getMediaFileId());

        //6.更新专辑包含声音数量
        AlbumInfo albumInfo = albumInfoMapper.selectById(trackInfo.getAlbumId());
        albumInfo.setIncludeTrackCount(albumInfo.getIncludeTrackCount() - 1);
        albumInfoMapper.updateById(albumInfo);
    }

    /**
     * 分页查询专辑下声音列表（动态判断每个声音付费标识）
     *
     * @param pageInfo 分页对象
     * @param userId   当前用户ID
     * @param albumId
     * @return
     */
    @Override
    public Page<AlbumTrackListVo> getAlbumTrackPage(Page<AlbumTrackListVo> pageInfo, Long userId, Long albumId) {
        ///1.分页查询声音列表，默认封装对象AlbumTrackListVo付费标识isShowPaidMark为false
        pageInfo = trackInfoMapper.getAlbumTrackPage(pageInfo, albumId);
        //2.根据专辑ID查询专辑信息得到：专辑付费类型、试听集数
        AlbumInfo albumInfo = albumInfoMapper.selectById(albumId);
        // 声音三种付费类型：0101-免费、0102-vip免费、0103-付费'
        String payType = albumInfo.getPayType();
        Integer tracksForFree = albumInfo.getTracksForFree();

        //3.动态判断(登录状态、身份、购买状态)每一页中每个声音付费标识
        //3.1 处理当前用户未登录
        if (userId == null) {
            //3.1.1 判断专辑付费类型：0102-vip免费、0103-付费
            if (SystemConstant.ALBUM_PAY_TYPE_VIPFREE.equals(payType) || SystemConstant.ALBUM_PAY_TYPE_REQUIRE.equals(payType)) {
                //3.1.2 将除去试听部分其他声音将付费标识全部改为true
                pageInfo.getRecords()
                        .stream()
                        // 过滤掉试听部分
                        .filter(t -> t.getOrderNum() > tracksForFree)
                        // 将付费标识全部设置为true
                        .forEach(t -> t.setIsShowPaidMark(true));
            }
        } else {
            //3.2 处理当前用户已登录
            //3.2.1 判断是会否为VIP，远程调用用户服务获取用户身份信息
            Boolean isVIP = false;
            UserInfoVo userInfoVo = userFeignClient.getUserInfoVo(userId).getData();
            if (userInfoVo.getIsVip().intValue() == 1 && userInfoVo.getVipExpireTime().after(new Date())) {
                isVIP = true;
            }
            //3.2.2 判断是否需要进一步查询声音购买状态
            Boolean isNeedCheckPayStatus = false;
            //条件1：如果是普通用户，查看专辑付费类型为VIP免费
            if (!isVIP && SystemConstant.ALBUM_PAY_TYPE_VIPFREE.equals(payType)) {
                isNeedCheckPayStatus = true;
            }
            //条件2：如果专辑付费类型为付费
            if (SystemConstant.ALBUM_PAY_TYPE_REQUIRE.equals(payType)) {
                isNeedCheckPayStatus = true;
            }
            //3.2.3 如果需要获取本页声音购买状态，远程调用用户服务获取购买状态
            if (isNeedCheckPayStatus) {
                //将本页中试听声音ID排除掉
                List<Long> needCheckPayStatusTrackIdList = pageInfo
                        .getRecords()
                        .stream()
                        //去掉试听声音
                        .filter(t -> t.getOrderNum() > tracksForFree)
                        .map(AlbumTrackListVo::getTrackId)
                        .collect(Collectors.toList());
                //远程调用用户服务获取本页中声音购买状态 1：已购买  0：未购买
                Map<Long, Integer> payStatusMap = userFeignClient.userIsPaidTrack(userId, albumId, needCheckPayStatusTrackIdList).getData();
                //3.2.4 根据响应本页中声音购买状态，动态设置付费标识，如果声音未购买则将付费标识设置为true
                pageInfo
                        .getRecords()
                        .stream()
                        //去掉试听声音
                        .filter(t -> t.getOrderNum() > tracksForFree)
                        .forEach(t -> {
                            //判断本页中声音购买状态，如果未购买则将付费标识设置为true
                            Integer payStatus = payStatusMap.get(t.getTrackId());
                            if (payStatus.intValue() == 0) {
                                t.setIsShowPaidMark(true);
                            }
                        });
            }
        }
        return pageInfo;
    }

    /**
     * 更新声音/专辑统计信息
     *
     * @param trackStatMqVo
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateTrackStat(TrackStatMqVo trackStatMqVo) {
        //TODO 避免大量写MySQL 考虑采用Redis计数器 没积攒100个更新后，再更新MySQL  稀释数据库写请求
        //1.更新声音统计数值
        LambdaUpdateWrapper<TrackStat> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(TrackStat::getTrackId, trackStatMqVo.getTrackId());
        updateWrapper.eq(TrackStat::getStatType, trackStatMqVo.getStatType());
        updateWrapper.setSql("stat_num = stat_num + " + trackStatMqVo.getCount());
        trackStatMapper.update(null, updateWrapper);
        //2.更新专辑统计数值（包括两个相同统计类型：播放量、评论量）
        if (SystemConstant.TRACK_STAT_PLAY.equals(trackStatMqVo.getStatType())) {
            LambdaUpdateWrapper<AlbumStat> albumStatUpdateWrapper = new LambdaUpdateWrapper<>();
            albumStatUpdateWrapper.eq(AlbumStat::getAlbumId, trackStatMqVo.getAlbumId());
            albumStatUpdateWrapper.eq(AlbumStat::getStatType, SystemConstant.ALBUM_STAT_PLAY);
            albumStatUpdateWrapper.setSql("stat_num = stat_num + " + trackStatMqVo.getCount());
            albumStatMapper.update(null, albumStatUpdateWrapper);
        }
        if (SystemConstant.TRACK_STAT_COMMENT.equals(trackStatMqVo.getStatType())) {
            LambdaUpdateWrapper<AlbumStat> albumStatUpdateWrapper = new LambdaUpdateWrapper<>();
            albumStatUpdateWrapper.eq(AlbumStat::getAlbumId, trackStatMqVo.getAlbumId());
            albumStatUpdateWrapper.eq(AlbumStat::getStatType, SystemConstant.ALBUM_STAT_COMMENT);
            albumStatUpdateWrapper.setSql("stat_num = stat_num + " + trackStatMqVo.getCount());
            albumStatMapper.update(null, albumStatUpdateWrapper);
        }
    }

    /**
     * 根据当前用户未购买声音数量动态渲染分集购买列表
     *
     * @param userId  用户ID
     * @param trackId 选择购买声音ID作为起始标准
     * @return [{name:"本集",price:0.5,trackCount:1},{name:"后10集",price:5,trackCount:10},{name:"全集",price:15,trackCount:30}]
     */
    @Override
    public List<Map<String, Object>> getUserFenJiPayList(Long userId, Long trackId) {
        List<Map<String, Object>> list = new ArrayList<>();
        //1.根据用户选择购买声音ID查询声音记录，将记录作为起始位置
        TrackInfo trackInfo = trackInfoMapper.selectById(trackId);
        Long albumId = trackInfo.getAlbumId();
        Integer orderNum = trackInfo.getOrderNum();

        //2.查询当前用户"未购买"(可能存在已购声音ID)声音列表 条件：专辑ID（等值）,序号（大于等于）
        LambdaQueryWrapper<TrackInfo> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(TrackInfo::getAlbumId, albumId);
        queryWrapper.ge(TrackInfo::getOrderNum, orderNum);
        queryWrapper.select(TrackInfo::getId);
        List<TrackInfo> waitBuyTrackList = trackInfoMapper.selectList(queryWrapper);

        //3.远程调用用户服务，查询指定专辑下当前用户已购声音ID列表
        List<Long> userPaidTrackIdList = userFeignClient.getUserPaidTrackIdList(albumId).getData();
        if (CollectionUtil.isNotEmpty(userPaidTrackIdList)) {
            //4.判断如果存在已购声音ID，将已购声音ID从"未购买“声音列表排除掉，得到真正未购声音列表
            waitBuyTrackList = waitBuyTrackList
                    .stream()
                    // 排除已购声音，如果声音ID存在已购声音ID列表中
                    .filter(t -> !userPaidTrackIdList.contains(t.getId()))
                    .collect(Collectors.toList());
        }

        //5.根据未购声音列表数量，动态构建分集购买列表
        int size = waitBuyTrackList.size();
        //5.1 根据专辑ID查询专辑信息，得到声音单价 专辑的价格类型：为单集购买，声音不支持折扣
        AlbumInfo albumInfo = albumInfoMapper.selectById(albumId);
        BigDecimal price = albumInfo.getPrice();
        //5.2 封装“本集”分集购买对象
        Map<String, Object> currMap = new HashMap<>();
        currMap.put("name", "本集");
        currMap.put("trackCount", 1);
        currMap.put("price", price);
        list.add(currMap);
        //5.3 封装其他分集购买对象 包括：后10集 后20集 全集
        for (int i = 10; i <= 50; i += 10) {
            //如果待购声音数量大于 i 显示 后i集
            if (size > i) {
                Map<String, Object> map = new HashMap<>();
                map.put("name", "后" + i + "集");
                map.put("trackCount", i);
                map.put("price", price.multiply(BigDecimal.valueOf(i)));
                list.add(map);
            } else {
                //如果待购声音数量小于 i 显示 全集 退出循环
                Map<String, Object> map = new HashMap<>();
                map.put("name", "全集("+size+"集)");
                map.put("trackCount", size);
                map.put("price", price.multiply(BigDecimal.valueOf(size)));
                list.add(map);
                break;
            }
        }
        return list;
    }

    /**
     * 查询用户指定购买数量未购买声音列表
     * @param userId 用户ID
     * @param trackId 选择购买声音ID，作为起始标准
     * @param trackCount 选择购买声音数量
     * @return
     */
    @Override
    public List<TrackInfo> getWaitBuyTrackInfoList(Long userId, Long trackId, Integer trackCount) {
        //1.根据选择购买声音ID，查询选购声音信息 得到专辑ID跟声音序号
        TrackInfo trackInfo = trackInfoMapper.selectById(trackId);
        Long albumId = trackInfo.getAlbumId();
        Integer orderNum = trackInfo.getOrderNum();

        //2.远程调用用户服务，查询该专辑用户已购声音ID列表
        List<Long> userPaidTrackIdList = userFeignClient.getUserPaidTrackIdList(albumId).getData();

        //3.查询当前用户"未购买"(可能存在已购声音ID)声音列表 条件：专辑ID（等值）,序号（大于等于）,已购声音ID排除掉, 限制查询数量
        LambdaQueryWrapper<TrackInfo> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(TrackInfo::getAlbumId, albumId);
        queryWrapper.ge(TrackInfo::getOrderNum, orderNum);
        if(CollectionUtil.isNotEmpty(userPaidTrackIdList)){
            //将已购声音ID排除掉
            queryWrapper.notIn(TrackInfo::getId, userPaidTrackIdList);
        }
        //限制查询数量 跟用户选择购买数量一致
        queryWrapper.last("limit "+trackCount);
        //指定查询列 避免暴露播放连接地址  将来要在订单服务中计算订单相关价格：获取声音价格，根据所属专辑ID查询对应单价
        queryWrapper.select(TrackInfo::getId, TrackInfo::getCoverUrl, TrackInfo::getTrackTitle, TrackInfo::getAlbumId);
        return trackInfoMapper.selectList(queryWrapper);
    }
}
